import tkinter as tk
from tkinter import ttk, messagebox, font
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib
import numpy as np
from gen import generate_input
from alg import KnightTour

# 配置matplotlib支持中文字体
matplotlib.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'KaiTi', 'FangSong', 'SimSun']
matplotlib.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题

class KnightTourUI:
    def __init__(self, root):
        self.root = root
        self.root.title("马踏棋盘求解器")
        self.root.geometry("800x600")
        self.root.resizable(True, True)
        
        # 创建默认字体
        self.default_font = font.nametofont("TkDefaultFont")
        self.default_font.configure(size=12)
        self.root.option_add("*Font", self.default_font)
        
        # 设置文本和标题字体
        self.text_font = font.Font(family="SimHei", size=12)
        self.title_font = font.Font(family="SimHei", size=14, weight="bold")
        
        # 配置ttk样式
        self.style = ttk.Style()
        self.style.configure("TLabel", font=self.text_font)
        self.style.configure("TButton", font=self.text_font)
        self.style.configure("TRadiobutton", font=self.text_font)
        self.style.configure("TEntry", font=self.text_font)
        self.style.configure("TSpinbox", font=self.text_font)
        self.style.configure("TLabelframe.Label", font=self.title_font)
        
        # 存储最近一次计算的数据，用于可视化
        self.last_calculation = None
        
        self.create_widgets()
    
    def create_widgets(self):
        # 创建主框架
        main_frame = ttk.Frame(self.root, padding="10")
        main_frame.pack(fill=tk.BOTH, expand=True)
        
        # 输入参数区域
        input_frame = ttk.LabelFrame(main_frame, text="输入参数", padding="10")
        input_frame.pack(fill=tk.X, padx=5, pady=5)
        
        # 棋盘尺寸和起始位置
        size_frame = ttk.Frame(input_frame)
        size_frame.pack(fill=tk.X, pady=5)
        
        ttk.Label(size_frame, text="棋盘大小 (n):").grid(row=0, column=0, padx=5, pady=5, sticky=tk.W)
        self.n_var = tk.IntVar(value=8)
        n_spinbox = ttk.Spinbox(size_frame, from_=5, to=15, textvariable=self.n_var, width=5)
        n_spinbox.grid(row=0, column=1, padx=5, pady=5)
        n_spinbox.bind("<FocusOut>", self.update_position_limits)
        
        ttk.Label(size_frame, text="起始位置 X:").grid(row=0, column=2, padx=5, pady=5, sticky=tk.W)
        self.start_x_var = tk.IntVar(value=0)
        self.start_x_spinbox = ttk.Spinbox(size_frame, from_=0, to=7, textvariable=self.start_x_var, width=5)
        self.start_x_spinbox.grid(row=0, column=3, padx=5, pady=5)
        
        ttk.Label(size_frame, text="起始位置 Y:").grid(row=0, column=4, padx=5, pady=5, sticky=tk.W)
        self.start_y_var = tk.IntVar(value=0)
        self.start_y_spinbox = ttk.Spinbox(size_frame, from_=0, to=7, textvariable=self.start_y_var, width=5)
        self.start_y_spinbox.grid(row=0, column=5, padx=5, pady=5)
        
        # 选择输入方式
        self.input_method = tk.StringVar(value="manual")
        ttk.Radiobutton(input_frame, text="自动生成起始位置", variable=self.input_method, value="auto", 
                       command=self.toggle_input_method).pack(anchor=tk.W, padx=5, pady=2)
        ttk.Radiobutton(input_frame, text="手动输入起始位置", variable=self.input_method, value="manual", 
                       command=self.toggle_input_method).pack(anchor=tk.W, padx=5, pady=2)
        
        # 操作按钮
        btn_frame = ttk.Frame(main_frame)
        btn_frame.pack(fill=tk.X, padx=5, pady=5)
        
        ttk.Button(btn_frame, text="求解", command=self.solve).pack(side=tk.LEFT, padx=5, pady=5)
        ttk.Button(btn_frame, text="可视化路径", command=self.show_visualization).pack(side=tk.LEFT, padx=5, pady=5)
        ttk.Button(btn_frame, text="重置", command=self.reset).pack(side=tk.LEFT, padx=5, pady=5)
        
        # 结果显示区域
        result_frame = ttk.LabelFrame(main_frame, text="计算结果", padding="10")
        result_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        self.result_text = tk.Text(result_frame, height=20, width=50, font=self.text_font)
        self.result_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        scrollbar = ttk.Scrollbar(result_frame, orient="vertical", command=self.result_text.yview)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.result_text.configure(yscrollcommand=scrollbar.set)
        
        # 初始化UI状态
        self.update_position_limits()
    
    def update_position_limits(self, event=None):
        n = self.n_var.get()
        max_idx = n - 1
        
        # 更新X和Y的可选范围
        self.start_x_spinbox.config(from_=0, to=max_idx)
        self.start_y_spinbox.config(from_=0, to=max_idx)
        
        # 如果当前值超出范围，调整为边界值
        if self.start_x_var.get() > max_idx:
            self.start_x_var.set(max_idx)
        if self.start_y_var.get() > max_idx:
            self.start_y_var.set(max_idx)
    
    def toggle_input_method(self):
        if self.input_method.get() == "auto":
            # 自动生成模式下禁用手动输入
            self.start_x_spinbox.config(state="disabled")
            self.start_y_spinbox.config(state="disabled")
        else:
            # 手动输入模式下启用输入框
            self.start_x_spinbox.config(state="normal")
            self.start_y_spinbox.config(state="normal")
    
    def solve(self):
        n = self.n_var.get()
        
        if self.input_method.get() == "auto":
            # 使用生成函数获取输入
            n, start_x, start_y = generate_input()
            # 更新UI显示
            self.n_var.set(n)
            self.start_x_var.set(start_x)
            self.start_y_var.set(start_y)
            self.update_position_limits()
        else:
            start_x = self.start_x_var.get()
            start_y = self.start_y_var.get()
        
        # 清空结果显示
        self.result_text.delete(1.0, tk.END)
        self.result_text.insert(tk.END, f"棋盘大小: {n}x{n}\n")
        self.result_text.insert(tk.END, f"起始位置: ({start_x}, {start_y})\n\n")
        
        # 求解问题
        kt = KnightTour(n)
        if kt.solve(start_x, start_y):
            board = kt.get_board()
            self.result_text.insert(tk.END, "找到解决方案！\n\n")
            
            # 在文本区域中显示棋盘
            for row in board:
                self.result_text.insert(tk.END, ' '.join(f"{cell:2d}" for cell in row) + "\n")
            
            # 存储计算结果供可视化使用
            self.last_calculation = {
                'n': n,
                'start_x': start_x,
                'start_y': start_y,
                'board': board
            }
        else:
            self.result_text.insert(tk.END, "未找到解决方案。\n")
            self.last_calculation = None
    
    def show_visualization(self):
        """在新窗口中显示马踏棋盘的可视化"""
        if self.last_calculation is None:
            messagebox.showinfo("提示", "请先求解问题再进行可视化")
            return
        
        n = self.last_calculation['n']
        board = self.last_calculation['board']
        
        # 创建新窗口
        vis_window = tk.Toplevel(self.root)
        vis_window.title("马踏棋盘路径可视化")
        vis_window.geometry("700x700")
        
        # 创建图形
        fig, ax = plt.subplots(figsize=(8, 8), dpi=100)
        canvas = FigureCanvasTkAgg(fig, master=vis_window)
        canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 添加关闭按钮
        btn_frame = ttk.Frame(vis_window)
        btn_frame.pack(fill=tk.X, padx=10, pady=10)
        ttk.Button(btn_frame, text="关闭", command=vis_window.destroy).pack(side=tk.RIGHT, padx=10)
        
        # 可视化棋盘和路径
        self.visualize_knight_tour(n, board, ax, canvas)
    
    def visualize_knight_tour(self, n, board, ax, canvas):
        """绘制马踏棋盘的路径"""
        # 清空图形
        ax.clear()
        
        # 从last_calculation中获取输入的起始位置
        start_x = self.last_calculation['start_x']
        start_y = self.last_calculation['start_y']
        
        # 创建棋盘
        ax.set_xlim(-0.5, n-0.5)
        ax.set_ylim(n-0.5, -0.5)  # 反转Y轴，使坐标系从上到下
        
        # 创建坐标映射数组，用于存储每个数字的坐标位置
        positions = {}
        
        # 绘制棋盘格子
        for i in range(n):
            for j in range(n):
                color = '#FFDDEE' if (i+j) % 2 == 0 else 'white'  # 淡粉色和白色
                # 直接使用i作为y坐标，不再反转
                y_pos = i
                ax.add_patch(plt.Rectangle((j-0.5, y_pos-0.5), 1, 1, color=color))
                
                # 获取该位置的移动顺序
                step = board[i][j]
                ax.text(j, y_pos, str(step), ha='center', va='center', fontsize=12, 
                       color='black')
                
                # 记录每个步骤数对应的坐标位置
                positions[step] = (j, y_pos)
        
        # 使用数字顺序构建路径
        path = []
        for step in range(n*n):  # 从0到n*n-1
            if step in positions:
                path.append(positions[step])
        
        # 绘制路径
        if path:
            path_x, path_y = zip(*path)
            ax.plot(path_x, path_y, color='#87CEFA', linestyle='-', linewidth=2, alpha=0.7)
            
            # 标记起点和终点
            ax.plot(path_x[0], path_y[0], 'go', markersize=12, label='起点')
            ax.plot(path_x[-1], path_y[-1], 'bo', markersize=12, label='终点')
            
            # 添加箭头指示移动方向
            for i in range(len(path)-1):
                dx = path_x[i+1] - path_x[i]
                dy = path_y[i+1] - path_y[i]
                ax.arrow(path_x[i], path_y[i], dx*0.8, dy*0.8, 
                        head_width=0.1, head_length=0.1, fc='#87CEFA', ec='#87CEFA', alpha=0.7)
        
        # 设置网格和标题
        ax.grid(True, linestyle='--', alpha=0.7)
        ax.set_title("马踏棋盘路径", fontsize=16)
        ax.set_xlabel("列", fontsize=14)
        ax.set_ylabel("行", fontsize=14)
        ax.legend(loc='upper right')
        
        # 设置刻度
        ax.set_xticks(range(n))
        ax.set_yticks(range(n))
        ax.set_xticklabels(range(n))
        ax.set_yticklabels(range(n))
        
        # 更新画布
        canvas.draw()
    
    def reset(self):
        """重置UI状态"""
        self.n_var.set(8)
        self.start_x_var.set(0)
        self.start_y_var.set(0)
        self.input_method.set("manual")
        self.toggle_input_method()
        self.update_position_limits()
        self.result_text.delete(1.0, tk.END)
        self.last_calculation = None

if __name__ == "__main__":
    root = tk.Tk()
    app = KnightTourUI(root)
    root.mainloop()
